/*
 * Copyright 2012-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.context.properties.bind;

import java.util.NoSuchElementException;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

/**
 * 用于返回 {@link Binder} 绑定操作结果的容器对象。可能包含成功绑定的对象，或者为空的结果。
 *
 * @param <T> 结果类型
 * @author Phillip Webb
 * @author Madhura Bhave
 * @since 2.0.0
 */
public final class BindResult<T> {

	private static final BindResult<?> UNBOUND = new BindResult<>(null);

	private final T value;

	private BindResult(T value) {
		this.value = value;
	}

	/**
	 * 返回已绑定的对象；如果没有绑定值，则抛出 {@link NoSuchElementException} 异常。
	 *
	 * @return 已绑定的值（永不为 {@code null}）
	 * @throws NoSuchElementException 如果没有绑定值，则抛出此异常
	 * @see #isBound()
	 */
	public T get() throws NoSuchElementException {
		if (this.value == null) {
			throw new NoSuchElementException("No value bound");
		}
		return this.value;
	}

	/**
	 * 如果有绑定结果，则返回 {@code true}。
	 *
	 * @return 是否有绑定结果
	 */
	public boolean isBound() {
		return (this.value != null);
	}

	/**
	 * 如果有绑定值，则调用指定的 consumer，否则不执行任何操作。
	 *
	 * @param consumer 有绑定值时执行的代码块
	 */
	public void ifBound(Consumer<? super T> consumer) {
		Assert.notNull(consumer, "Consumer must not be null");
		if (this.value != null) {
			consumer.accept(this.value);
		}
	}

	/**
	 * 对绑定值应用提供的映射函数；如果没有绑定值，则返回一个更新后的未绑定结果。
	 *
	 * @param <U> 映射函数结果的类型
	 * @param mapper 应用于绑定值的映射函数；如果没有绑定值，映射函数不会被调用
	 * @return 一个描述对当前 {@code BindResult} 的值应用映射函数结果的 {@code BindResult}
	 */
	public <U> BindResult<U> map(Function<? super T, ? extends U> mapper) {
		Assert.notNull(mapper, "Mapper must not be null");
		return of((this.value != null) ? mapper.apply(this.value) : null);
	}

	/**
	 * 返回已绑定的对象；如果没有绑定值，则返回参数 {@code other}（可为 {@code null}）。
	 *
	 * @param other 无绑定值时返回的值
	 * @return 已绑定的值，或者 {@code other}
	 */
	public T orElse(T other) {
		return (this.value != null) ? this.value : other;
	}

	/**
	 * 返回已绑定的对象；如果没有绑定值，则返回调用 {@code other} 获得的结果。
	 *
	 * @param other 当没有绑定值时提供返回值的 {@link Supplier}
	 * @return 已绑定的值；如果无绑定值，则返回 {@code other} 提供的值
	 */
	public T orElseGet(Supplier<? extends T> other) {
		return (this.value != null) ? this.value : other.get();
	}

	/**
	 * 返回已绑定的对象；如果没有绑定值，则抛出由提供的异常供应器创建的异常。
	 *
	 * @param <X> 要抛出的异常类型
	 * @param exceptionSupplier 用于返回要抛出的异常的供应器
	 * @return 当前存在的值
	 * @throws X 如果没有绑定值，则抛出该异常
	 */
	public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
		if (this.value == null) {
			throw exceptionSupplier.get();
		}
		return this.value;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null || getClass() != obj.getClass()) {
			return false;
		}
		return ObjectUtils.nullSafeEquals(this.value, ((BindResult<?>) obj).value);
	}

	@Override
	public int hashCode() {
		return ObjectUtils.nullSafeHashCode(this.value);
	}

	@SuppressWarnings("unchecked")
	static <T> BindResult<T> of(T value) {
		if (value == null) {
			return (BindResult<T>) UNBOUND;
		}
		return new BindResult<>(value);
	}

}
